// 初始化时区
const initMomentConfig = () => {
    if (!window.moment || !window.moment.tz) {
        console.error('依赖未加载！');
        return;
    }
    window.moment.tz.setDefault('Asia/Shanghai');
}
initMomentConfig();

// 获取上海时间（直接使用时区转换）
const getShanghaiTime = (format = '') => {
    const shTime = window.moment();
    return format ? shTime.format(format) : shTime;
}

// 时间有效性检测
const isTimeValid = (time) => {
  return time && window.moment(time).isValid() && time !== '0001-01-01T00:00:00Z';
}

// 比较开始时间
const isAfterShanghaiTime = (targetTime) => {
    return window.moment(targetTime).isAfter(getShanghaiTime())
}

//比较结束时间
const isBeforeShanghaiTime = (targetTime) => {
    return window.moment(targetTime).isBefore(getShanghaiTime())
}

const getFileName = (url) => {
    if (!url || typeof url !== 'string') return
    const filename = url.split('/').pop().split(/[?#]/)[0].replace(/%20/g, ' ')
    return filename || '未命名文件'
}

// 生成IV
const generateIV = (key) => {
	const hasher = CryptoJS.SHA256(key);
	return hasher.toString(CryptoJS.enc.Hex).substring(0, 32); // 取前32个字符作为IV
};

// 加密
const aesEncrypt = (data, key) => {
	if (key.length > 32) {
		key = key.slice(0, 32);
	}
	const cypherKey = CryptoJS.enc.Utf8.parse(key);
	const iv = CryptoJS.enc.Hex.parse(generateIV(key));
	const cfg = {
		iv: iv,
		mode: CryptoJS.mode.CBC,
		padding: CryptoJS.pad.ZeroPadding
	};
	return CryptoJS.AES.encrypt(data, cypherKey, cfg).toString();
};

// 解密
const aesDecrypt = (data, key) => {
	if (key.length > 32) {
		key = key.slice(0, 32);
	}
	const cypherKey = CryptoJS.enc.Utf8.parse(key); // 解析密钥
	const iv = CryptoJS.enc.Hex.parse(generateIV(key)); // 解析 IV
	const cfg = {
		iv: iv,
		mode: CryptoJS.mode.CBC,
		padding: CryptoJS.pad.ZeroPadding // 使用 ZeroPadding
	};
	// 解密数据
	const decrypted = CryptoJS.AES.decrypt(data, cypherKey, cfg);
	// 将解密后的数据转换为 UTF-8 字符串
	return decrypted.toString(CryptoJS.enc.Utf8);
};

// 会话管理
const sessionManager = {
    clearSession() {
        localStorage.removeItem('session_id');
        localStorage.removeItem('session_key');
    },
    async getSessionKey() {
        const sessionID = localStorage.getItem('session_id');
        const storedKey = localStorage.getItem('session_key');
        // 强制清除过期的会话（示例：开发阶段临时启用）
        //if (sessionID) this.clearSession();
        if (sessionID && storedKey) {
            return { sessionID, sessionKey: storedKey };
        }
        try {
            const { data } = await api.post('getsession');
            // 严格校验响应格式
            if (!data.data?.session_id || !data.data?.session_key) {
                throw new Error('无效的会话响应格式');
            }
            // 存储会话
            localStorage.setItem('session_id', data.data.session_id);
            localStorage.setItem('session_key', data.data.session_key);
            return { 
                sessionID: data.data.session_id, 
                sessionKey: data.data.session_key 
            };
        } catch (err) {
            this.clearSession();
            console.error('会话获取失败:', err);
            throw new Error('无法建立安全会话');
        }
    }
};

// 会话获取
const getSessionWithRetry = async (maxRetries = 2) => {
    let retryCount = 0;
    while (retryCount <= maxRetries) {
        try {
            const { sessionID, sessionKey } = await sessionManager.getSessionKey();
            if (!sessionID || !sessionKey) {
                throw new Error('会话密钥丢失，请重新获取');
            }
            return { sessionID, sessionKey };
        } catch (err) {
            retryCount++;
            console.error(`获取会话密钥失败，第${retryCount}次重试:`, err.message);
            sessionManager.clearSession();
            if (retryCount > maxRetries) {
                throw new Error('会话密钥获取失败，请重试');
            }
        }
    }
};

// 发送请求
const sendRequest = async (url, method, sessionID, sessionKey, requestData, headers = {}) => {
	try {
		if (window.APP_CONFIG.baseurl == 'https://zp.kzhgzs.cn/') console.log('演示站调试请求：'+url, requestData);
		const encryptedData = aesEncrypt(JSON.stringify(requestData), sessionKey);
		const res = await api.request({
			url: url,
			method,
			data: encryptedData,
			headers: {
				'X-Session-ID': sessionID,
				...headers
			}
		});
		if (res.status === 406) {
			// 会话过期，清除会话信息并要求重试
			sessionManager.clearSession();
			return { needRetry: true, requestData };
		}
		if (res.status !== 200) {
		    if(res.data?.error) {
		        throw new Error(res.data.error);
		    }else{
			    throw new Error(`请求失败，状态码: ${res.status}`);
		    }
		}
		const decrypted = aesDecrypt(res.data, sessionKey);
		const result = JSON.parse(decrypted);
		if (window.APP_CONFIG.baseurl == 'https://zp.kzhgzs.cn/') console.log('演示站调试返回：', result);
		if (result.code !== 200) {
			let error = new Error(result.message || '请求失败');
			error.code = result.code; // 附加 code 属性
			throw error;
		}
		return { needRetry: false, data: result.data };
	} catch (err) {
		// 处理超时（axios 超时错误码为 ECONNABORTED）
        if (err.code === 'ECONNABORTED' || err.message.includes('timeout')) {
            ElMessage.error('请求超时，请稍后再试');
            return { needRetry: false };
        }
		throw err;
	}
};

// 封装完整请求流程
const myRequest = async (option) => {
    let loadingInstance;
    let sessionID, sessionKey;
    let retryCount = 0;
    const maxRetries = 2; // 设置最大重试次数
    try {
		if (option.showLoading) {
            loadingInstance = ElLoading.service({
                lock: true,
                text: option.title || '数据加载中',
                background: 'rgba(0, 0, 0, 0.7)'
            });
        }
        // 获取会话密钥（最多重试2次）
        ({ sessionID, sessionKey } = await getSessionWithRetry());
        // 请求逻辑：发送请求并处理解密结果
        let { needRetry, data, requestData } = await sendRequest(option.url, option.method || 'GET', sessionID, sessionKey, option.data || {}, option.headers);
        // 如果会话过期则重新获取密钥并重试请求
        while (needRetry && retryCount < maxRetries) {
            retryCount++;
            // 重新获取会话密钥并重试
            ({ sessionID, sessionKey } = await getSessionWithRetry());
            const retryResult = await sendRequest(option.url, option.method || 'GET', sessionID, sessionKey, requestData, option.headers);
            // 如果还是失败则继续重试
            if (!retryResult.needRetry) {
                return retryResult.data;
            }
        }
        // 如果重试次数超过最大值，抛出错误
        if (retryCount >= maxRetries) {
            throw new Error('重试次数超过最大限制，无法获取有效会话密钥');
        }
        return data; // 返回数据
    } catch (err) {
         console.error('请求错误:', err);
        if (err.code === 401) {
            localStorage.removeItem('token'); // 清除 Token
            localStorage.removeItem('user');
            ElMessage.error(err.message || '请求失败，请稍后再试');
            // 跳转到登录页并携带当前路径
            const router = window.RouterConfig.router;
            const currentPath = router.currentRoute.value.fullPath;
            const redirectPath = currentPath.startsWith('/login') ? '/' : currentPath;
            setTimeout(() => {
                router.push({
                    name: 'login',
                    query: { 
                        redirect: encodeURIComponent(redirectPath) // 编码防止特殊字符
                    }
                });
            }, 200);
        } else {
            ElMessage.error(err.message || '请求失败，请稍后再试');
        }
        throw err;
    } finally {
		// 关闭加载状态
        if (option.showLoading && loadingInstance) {
            loadingInstance.close();
        }
    }
};

// axios 基础配置
const api = axios.create({  
    baseURL: window.APP_CONFIG.baseurl + 'v1/',
    headers: {
        'Content-Type': 'text/plain', // text/plain避免自动JSON处理
        'X-Session-ID': '',
    },
    // 允许处理所有状态码（不自动抛出错误）
    validateStatus: function (status) {
        return status >= 200 && status < 500;
    },
    transformRequest: [data => data] // 禁用数据转换
});

// 设置请求拦截器，为每个请求添加Token
api.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token') || '';
    if (token) config.headers.Authorization = `Bearer ${token}`;
    return config;  
}, function (error) {
    return Promise.reject(error);
});

//请求函数封装
const axiospost = async (url, data, method='POST', showLoading = false) => {
    return myRequest({
        url,
        method,
        data,
        showLoading,
        title: '正在加载'
    });
}

// 身份证输入处理工具函数
const extractIdCardInfo = (idCard) => {
    let birthdate = `${idCard.substr(6,4)}-${idCard.substr(10,2)}-${idCard.substr(12,2)}`;
    return {
        gender: parseInt(idCard[16]) % 2 === 0 ? '女' : '男',
        birthdate: birthdate,
        old: calculateAge(birthdate)
    }
}

// 计算周岁
const calculateAge = (birthDate) => {
	const today = new Date();
	const birthYear = parseInt(birthDate.slice(0, 4), 10);
	const birthMonth = parseInt(birthDate.slice(4, 6), 10) - 1;
	const birthDay = parseInt(birthDate.slice(6), 10);
	const birth = new Date(birthYear, birthMonth, birthDay);
	let age = today.getFullYear() - birth.getFullYear();
	// 检查是否已经过了生日
	const todayMonth = today.getMonth();
	const todayDay = today.getDate();
	if (todayMonth < birthMonth || (todayMonth === birthMonth && todayDay < birthDay)) {
		age--;
	}
	return age;
}

const lx = [{
		value: 'text',
		label: '单行文本'
	},
	{
		value: 'textarea',
		label: '多行文本'
	},
	{
		value: 'select',
		label: '单选下拉'
	},
	{
		value: 'muselect',
		label: '多选下拉'
	},
	{
		value: 'radio',
		label: '单选框'
	},
	{
		value: 'checkbox',
		label: '多选框'
	},
	{
		value: 'date',
		label: '日期'
	},
	{
		value: 'datetime',
		label: '日期时间'
	},
	{
		value: 'mobile',
		label: '手机号'
	},
	{
		value: 'idcard',
		label: '身份证号'
	},
	{
		value: 'number',
		label: '数字'
	},
	{
		value: 'image',
		label: '单图上传'
	},
	{
		value: 'multiimage',
		label: '多图上传'
	},
	{
		value: 'file',
		label: '单文件上传'
	},
	{
		value: 'multifile',
		label: '多文件上传'
	},
	{
		value: 'video',
		label: '视频上传'
	},
	{
		value: 'area',
		label: '省市县联动'
	},
	{
		value: 'map',
		label: '地图定位'
	},
	{
		value: 'rowcalc',
		label: '行内计算'
	},
	{
		value: 'stringconcat',
		label: '字符拼接'
	},
	{
		value: 'randomkey',
		label: '随机密钥'
	},
	{
		value: 'subtable',
		label: '子表'
	},
	{
	    value: 'linkselect',
	    label: '联动下拉'
	},
    {
        value: 'richtext',
        label: '富文本',
    },
	{
		value: 'sczs',
		label: '生成证书'
	},
	{
		value: 'word',
		label: '生成word'
	}];

// 创建全局语音对象
window.SpeechManager = (() => {
	let isIOSInitialized = false
	const initForIOS = () => {
		if (!isIOSInitialized) {
			const dummy = new SpeechSynthesisUtterance('')
			window.speechSynthesis.speak(dummy)
			window.speechSynthesis.cancel()
			isIOSInitialized = true
		}
	}
	const speak = (text, options) => {
		return new Promise((resolve) => {
			const utterance = new SpeechSynthesisUtterance(text)
			Object.assign(utterance, {
				lang: 'zh-CN',
				rate: 1,
				pitch: 1,
				...options,
				onend: resolve
			})
			window.speechSynthesis.speak(utterance)
		})
	}
	const stop = () => window.speechSynthesis.cancel()
	return {
		initForIOS,
		speak,
		stop
	}
})()

// 字体映射表
const systemFontMap = {
    'uploads/fonts/simsun.ttc': 'SimSun, 宋体', // Windows + 中文系统
    'uploads/fonts/microsoftyahei.ttc': 'Microsoft YaHei, 微软雅黑', 
    'uploads/fonts/simhei.ttf': 'SimHei, 黑体',
    'uploads/fonts/simkai.ttf': 'KaiTi, 楷体',
    'uploads/fonts/fangsong.ttf': 'FangSong, 仿宋',
    'uploads/fonts/xinkai.ttf': 'STXingkai, 华文行楷', // macOS 华文字体
    'uploads/fonts/suti.ttf': 'SimSun, 舒体',
    'uploads/fonts/xbs.ttf': 'STZhongsong, 华文中宋',
    'uploads/fonts/dbs.ttf': 'STZhongsong, 华文中宋',
    'uploads/fonts/arial.ttf': 'Arial, sans-serif' // 兜底
};

// 文件名提取方法
const extractFileName = (path) => {
    try {
        const url = new URL(path)
        return url.pathname.split('/').pop().split('?')[0].toLowerCase() // 去除查询参数
    } catch {
        return path.split('/').pop().split('?')[0].toLowerCase()
    }
}

//虚拟表格列--------------------------------------------------------------------------------
const getcolumns = (tempjson) => {
    let temp = [];
    if (tempjson && tempjson.length > 0) {
        for (let k in tempjson[0]) {
            temp.push({
                key: k,
                dataKey: k,
                title: k,
                width: 200,
                cellRenderer: ({ cellData }) => {
                    if (isImageUrl(cellData)) {
                        return h('img', {
                            src: cellData,
                            style: 'width: 50px; height: 50px; object-fit: cover; cursor: pointer;',
                            on: {
                                click: () => window.open(cellData) // 添加预览功能
                            }
                        });
                    }
                    return h('div', { class: 'cell-text' }, cellData);
                }
            });
        }
    }
    return temp;
};
            
//虚拟表格数据
const gettempdata = (tempjson) => {
    if(tempjson && tempjson.length > 0) {
        return tempjson.map((item, rowIndex) => {
            const rowData = {
                id: `row-${rowIndex}`,
                parentId: null,
                ...item
            };  
            return rowData;  
        });
    }
    return [];
}

//获取表头行行号
const getHeaderRowIndex = (worksheet) => {
	// 初始化表头行索引和数据行数组
	let headerRowIndex = 0; 
	let maxNonEmptyCells = 0;
	let range = XLSX.utils.decode_range(worksheet['!ref']); // 获取工作表的引用范围
	// 遍历工作表前5行
	for (let R = range.s.r; R <= Math.min(range.e.r, range.s.r + 4); ++R) {
		let nonEmptyCells = 0;
		// 遍历当前行的每个单元格
		for (let C = range.s.c; C <= range.e.c; ++C) {
			const cellRef = XLSX.utils.encode_cell({r: R, c: C});
			if (worksheet[cellRef] && worksheet[cellRef].t !== undefined) {
				nonEmptyCells++;
			}
		}
		// 如果当前行的非空单元格数量多于之前的记录，则更新表头行索引
		if (nonEmptyCells > maxNonEmptyCells) {
			maxNonEmptyCells = nonEmptyCells;
			headerRowIndex = R;
		}
	}
	return headerRowIndex;
};

const getValidFile = (e) => {
    const file = e.raw || e; // 兼容不同上传组件的参数结构
    if (!file) {
        throw new Error('请选择有效文件');
    }
    return file;
};

const readBrowserFile = (file) => {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = e => {
            // 强制转换为 Uint8Array 格式
            const arrayBuffer = e.target.result;
            resolve(new Uint8Array(arrayBuffer));
        };
        reader.onerror = e => reject(e.target.error);
        reader.readAsArrayBuffer(file); // 保持 ArrayBuffer 读取
    });
};

// 科学计数法验证工具函数
const isScientificNotation = (str) => {
    return /^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)$/i.test(str);
};

// 科学计数法转完整数字字符串
const scientificToDecimal = (value) => {
    if (typeof value !== 'string') return value;
    const sciPattern = /^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)$/i;
    if (!sciPattern.test(value)) return value;
    const [base, exponent] = value.toLowerCase().split('e');
    let [integer, fraction = ''] = base.split('.');
    let exp = parseInt(exponent, 10);
    integer = integer.replace(/^0+/, '') || '0'; // 移除前导零
    fraction = fraction.replace(/0+$/, ''); // 移除尾随零
    if (exp > 0) {
        if (exp <= fraction.length) {
            integer += fraction.substring(0, exp);
            fraction = fraction.substring(exp);
        } else {
            integer += fraction;
            const remainingZeros = exp - fraction.length;
            integer += '0'.repeat(remainingZeros);
            fraction = '';
        }
    } else if (exp < 0) {
        exp = -exp;
        const padLength = exp - integer.length;
        if (padLength > 0) {
            fraction = '0'.repeat(padLength) + integer + fraction;
            integer = '0';
        } else {
            fraction = integer.slice(-exp) + fraction;
            integer = integer.slice(0, -exp) || '0';
        }
    }
    return integer + (fraction ? `.${fraction}` : '');
};

// 读取表格
const readExcelFile = async (file) => {
	const arrayBuffer = await readBrowserFile(file); 
    const { files, ids } = await readImages(arrayBuffer); //获取嵌入式图片
    const workbook = XLSX.read(arrayBuffer, { type: 'array', cellText: true, cellDates: true });
	var sheetNames = workbook.SheetNames;
	var worksheet = workbook.Sheets[sheetNames[0]];
	let headerRowIndex = getHeaderRowIndex(worksheet);
    const sheetJSONOpts = {
        range: headerRowIndex,
        raw: false, // 保持原始数据类型（默认为false）
        blankrows: false, // 是否保留空白行 
        defval: '', // 默认值为空字符串 
        cellText: true, // 强制将所有单元格内容转换为字符串 
        cellDates: false, // 关闭日期格式转换 
        type: 'text' // 指定读取类型为文本
    };
    let temp = XLSX.utils.sheet_to_json(worksheet, sheetJSONOpts);
    if(temp.length == 0) throw new Error('表格数据为空');
	let filteredJson = [];
	for (let row of temp) {
		for (const key in row) {
			let t = key.toString().trim();
			if (isSpecialEmpty(t)) {
				delete row[key]; //如果key有空值则删除该行
			}
			if (row[key]) {
                // 处理科学计数法
                if (typeof row[key] === 'string' && isScientificNotation(row[key])) {
                    const decimalStr = scientificToDecimal(row[key]);
                    // 检查是否为整数且超过15位
                    if (/^\d+$/.test(decimalStr) && decimalStr.length > 15) {
                        row[key] = decimalStr;
                    }
                    row[key] = decimalStr; // 更新value为转换后的字符串
                }
                // 处理单元格内嵌图片 row[key] = DISPIMG("ID_DC919996C4B843209A854B233823EC7A",1)
				let image = '';
				const imgid = extractIDFromFormula(row[key]);
				if (imgid) {
					let imgname = ids[imgid];
					if (imgname) image = files[imgname];
				}
				if (image) row[key] = image;
			}
		}
		let filteredValues = Object.values(row).filter(value => value != null && value != '' && value != undefined);
		if (Object.keys(row).length > 1 && filteredValues.length > 1) {
			filteredJson.push(row);
		}
	}
	return filteredJson;
};

// 读取图片
const readImages = async (buffer) => {
	const zip = new JSZip();
	const x2js = new X2JS();
    // 明确指定格式为 uint8array
    const result = await zip.loadAsync(buffer, { 
        optimizedBinaryString: true,  // 启用二进制优化模式
        createFolders: false          // 禁用自动创建文件夹
    });
	const imagesTarget = {};
	const imagesTarResult = {};
	const imageList = {};
    console.log('并行上传，统一等待');
    const processPromises = [];
    for (const key in result.files) {
        if (key.includes("media/image") && !result.files[key].dir) {
            processPromises.push(processFile(result, key, zip, imageList));
        }
    }
    await Promise.all(processPromises);
    console.log('所有图片上传完成');
	if (result.files['xl/_rels/cellimages.xml.rels'] && result.files['xl/cellimages.xml']) {
		const template1 = result.files['xl/_rels/cellimages.xml.rels'];
		const template2 = result.files['xl/cellimages.xml'];
		const xml = await template1.async('string');
		const xml2 = await template2.async('string');
		let temp = x2js.xml2js(xml); //转为json格式
		let temp2 = x2js.xml2js(xml2);
		let Relationship = temp.Relationships.Relationship;
		if (!Array.isArray(Relationship)) {
			Relationship = [Relationship];
		}
		Relationship.forEach(cell => {
			imagesTarget[cell._Id] = extractRelativePath(cell._Target);
		});
		let cellImage = temp2.cellImages.cellImage;
		if (!Array.isArray(cellImage)) {
			cellImage = [cellImage];
		}
		cellImage.forEach(cell => {
			imagesTarResult[cell.pic.nvPicPr.cNvPr._name] = imagesTarget[cell.pic.blipFill
				.blip['_r:embed']];
		});
	}
	return {
		files: imageList,
		ids: imagesTarResult
	};
};

//批量上传图片到后台
const processFile = async (result, key, zip, imageList) => {
    const base64Data = await zip.file(result.files[key].name).async('base64');
    let imageType;
    if (key.endsWith('.png')) {
        imageType = 'png';
    } else if (key.endsWith('.jpeg') || key.endsWith('.jpg')) {
        imageType = 'jpg';
    }
    const name = extractRelativePath(result.files[key].name); 
    const now = new Date();
    const dateString = `${now.getFullYear()}${(now.getMonth()+1).toString().padStart(2,'0')}${now.getDate().toString().padStart(2,'0')}`;
    try {
        const res = await axios({
            url: `${window.APP_CONFIG.baseurl}v1/base64toimg`,
            method: 'post',
            headers: {'Content-Type': 'application/json'},
            data: {
                type: imageType,
                img: base64Data,
                path: `images/${dateString}/`
            }
        });
        if (res.data?.data) {
            imageList[name] = res.data.data;
        }
    } catch (err) {
        console.error(`图片 ${name} 上传失败:`, err);
        imageList.set(name, 'error-image.jpg');
    }
};

//取消图片前缀
const extractRelativePath = (fullPath) => {
    // 处理不同格式路径（如 '../media/image1.png' 或 'xl/media/image1.png'）
    const parts = fullPath.split(/[\/\\]/);
    const mediaIndex = parts.findIndex(part => part.toLowerCase() === 'media');
    if (mediaIndex !== -1) {
        return parts.slice(mediaIndex).join('/');
    }
    return fullPath; // 非媒体文件路径直接返回（理论上不会执行到这里）
};

// 判断是否为嵌入式图片
const extractIDFromFormula = (formula) => {
	if (!formula) return null;
	const match = formula.match(/"([^"]+)"/); // 正则表达式匹配双引号之间的内容
	return match ? match[1] : null;
};

// 检查单元格值是否为空
const isSpecialEmpty = (value) => {
	if (typeof value !== 'string') {
		return value == null || value === '';
	}
	return value.trim() === '__EMPTY' || /^\s*\_\_EMPTY\_\d+\s*$/.test(value);
};

// ai生成表单-----------------------------------------------------------------------------------------------------------
const FIELD_TYPE_CONFIG = [
    { 
        value: 'text', 
        text: '单行文本', 
        keywords: ['姓名', '名称', '标题', '账号', '邮箱', '邮编', '联系人', '企业名称'] 
    },
    { 
        value: 'textarea', 
        text: '多行文本', 
        keywords: ['地址', '简介', '描述', '备注', '评价', '审批意见', '公司简介', '产品说明'] 
    },
    { 
        value: 'select', 
        text: '单选下拉', 
        keywords: ['类型', '分类', '省份', '城市', '学历', '行业', '部门', '优先级', '审批状态'] 
    },
    { 
        value: 'muselect', 
        text: '多选下拉', 
        keywords: ['标签', '技能', '权限', '适用门店', '产品分类', '擅长领域', '过敏史'] 
    },
    { 
        value: 'radio', 
        text: '单选框', 
        keywords: ['性别', '是否', '婚姻状况', '是否公开', '是否开发票', '支付方式'] 
    },
    { 
        value: 'checkbox', 
        text: '多选框', 
        keywords: ['爱好', '兴趣', '症状', '服务协议', '用户协议', '订阅内容'] 
    },
    { 
        value: 'date', 
        text: '日期', 
        keywords: ['生日', '日期', '入职日期', '预约日期', '合同开始日', '有效期至'] 
    },
    { 
        value: 'datetime', 
        text: '日期时间', 
        keywords: ['创建时间', '更新时间', '会议时间', '考试时间', '发货时间'] 
    },
    { 
        value: 'mobile', 
        text: '手机号', 
        keywords: ['手机', '联系电话', '紧急联系人', '客服电话', '推荐人手机'] 
    },
    { 
        value: 'idcard', 
        text: '身份证号', 
        keywords: ['身份证', '法人证件', '监护人证件', '经办人身份证'] 
    },
    { 
        value: 'number', 
        text: '数字', 
        keywords: ['年龄', '数量', '金额', '库存', '评分', '工龄', '身高(cm)', '体重(kg)'] 
    },
    { 
        value: 'image', 
        text: '单图上传', 
        keywords: ['头像', '证件照', '营业执照', '合同封面', '产品主图'] 
    },
    { 
        value: 'multiimage', 
        text: '多图上传', 
        keywords: ['案例', '作品集', '检测报告', '资质证书', '现场照片', '商品详情图'] 
    },
    { 
        value: 'file', 
        text: '文件上传', 
        keywords: ['附件', '合同文件', '简历', '设计方案', '验收文档'] 
    }
];

// 增强帮助提示系统
const getHelpText = (field) => {
    const helpMap = {
        mobile: '请输入11位手机号码（格式示例：13800138000）',
        idcard: '请输入18位有效身份证号码（最后一位允许X）',
        number: '请输入有效数字（允许两位小数）',
        image: '支持JPG/PNG/WebP格式，大小不超过5MB',
        multiimage: '最多上传9张图片，每张不超过5MB',
        date: '格式：YYYY-MM-DD',
        datetime: '格式：YYYY-MM-DD HH:mm',
        file: '支持PDF/DOCX/XLSX格式，单个文件不超过20MB'
    };
    return helpMap[field.type] || `${field.name}（${field.type === 'textarea' ? '最多500字' : '最多50字'}）`;
};

// 默认选项预设
const getDefaultOptions = (fieldName) => {
    const optionPresets = {
        // 基础信息
        '性别': '男/女/保密',
        '学历': '初中/高中/大专/本科/硕士/博士',
        '婚姻状况': '未婚/已婚/离异/丧偶',
        '血型': 'A型/B型/O型/AB型',
        '政治面貌': '群众/共青团员/中共党员/民主党派',

        // 系统通用
        '状态': '启用/禁用/待审核/已过期',
        '优先级': '紧急/高/中/低',
        '是否': '是/否',
        '开关状态': '开启/关闭',
        '操作类型': '新增/修改/删除/查询',

        // 地址相关
        '省份': '北京/上海/广东/浙江/江苏/其他',
        '城市等级': '一线城市/新一线城市/二线城市/三线及以下',
        '快递公司': '顺丰/中通/圆通/申通/EMS/京东物流',

        // 业务类型
        '行业': 'IT互联网/金融/教育/医疗/制造业/零售/餐饮/物流',
        '职位类型': '技术类/产品类/运营类/市场类/行政类/其他',
        '客户等级': 'VIP客户/普通客户/潜在客户/流失客户',
        '订单状态': '待支付/待发货/已发货/已完成/已取消',
        '支付方式': '微信支付/支付宝/银联/现金/信用卡/数字货币',
        '发票类型': '增值税普票/增值税专票/电子发票/无需发票',

        // 商品管理
        '产品分类': '电子产品/家居用品/服饰鞋包/美妆个护/食品饮料',
        '规格单位': '件/套/箱/千克/毫升/米',
        '上架状态': '在售/下架/库存预警',

        // 人力资源
        '工作经验': '应届生/1-3年/3-5年/5-10年/10年以上',
        '薪资范围': '面议/5k-8k/8k-15k/15k-25k/25k以上',
        '到岗时间': '随时/1周内/2周内/1个月内',

        // 医疗健康
        '过敏史': '无/青霉素/花粉/海鲜/尘螨/其他',
        '症状类型': '发热/咳嗽/头痛/腹泻/皮疹/其他',
        '用药频率': '每日一次/每日两次/每周一次/按需服用',

        // 内容管理
        '内容类型': '文章/视频/图片/音频/混合内容',
        '审核状态': '待审核/已通过/已驳回/需修改',
        '推荐级别': '置顶推荐/首页推荐/栏目推荐/不推荐',

        // 国际化
        '语言': '中文(简体)/中文(繁体)/English/日本語',
        '时区': 'GMT+8 中国标准/UTC 国际标准/GMT-5 北美东部'
    };
    return optionPresets[fieldName] || '选项1/选项2/选项3';
};

// 增强类型推断算法
const getAutoType = (fieldName) => {
    // 增加权重机制和排除词
    const excludeWords = ['说明', '描述', '备注'];
    const cleanName = fieldName.replace(new RegExp(`(${excludeWords.join('|')})`, 'g'), '');

    // 精确匹配优先
    const exactMatch = FIELD_TYPE_CONFIG.find(item => 
        item.keywords.some(kw => cleanName === kw)
    );
    if (exactMatch) return exactMatch.value;

    // 带权重的模糊匹配
    const weightedMatches = FIELD_TYPE_CONFIG.map(type => ({
        type: type.value,
        score: type.keywords.reduce((acc, kw) => 
            acc + (cleanName.includes(kw) ? 2 : 0) + 
            (cleanName.startsWith(kw) ? 1 : 0) + 
            (cleanName.endsWith(kw) ? 1 : 0), 0)
    }));

    const bestMatch = weightedMatches.reduce((a, b) => 
        b.score > a.score ? b : a, { score: 0 }
    );

    return bestMatch.score > 0 ? bestMatch.type : 'text';
};

// 占位符系统
const getPlaceholder = (field) => {
    const placeholders = {
        radio: '请选择（单选）',
        select: '请选择（单选）',
        muselect: '请选择（可多选）',
        date: '选择日期',
        datetime: '选择日期和时间',
        image: '点击上传图片',
        multiimage: '点击上传多张图片',
        file: '点击上传文件'
    };
    return placeholders[field.type] || `${field.type === 'textarea' ? '请输入...' : '请输入'}`;
};

//JSON修复方法
const fixJsonFormat = (str) => {
    return str.replace(/[\[\]{}``]|字段列表|：|\\/g, '').replace(/"+(\w+)"+/g, '"$1"').replace(/"\s*"/g, '","')   .replace(/[“”‘’]/g, '"').replace(/(^|,)\s*([^"\s])/g, '$1"$2').replace(/([^"\s])(,|$)/g, '$1"$2').replace(/^\s*/, '[').replace(/\s*$/, ']').replace(/"QQ"\/微信/g, '"QQ/微信"').replace(/,+/g, ',').replace(/",\s*"/g, '","').replace(/\[\s*"/, '["').replace(/"\s*\]/, '"]');
};

// 校验逻辑
const validateStructure = (data) => {
    // 处理字符串数组情况
    if (Array.isArray(data) && data.every(item => typeof item === 'string')) {
        return {
            value: data.map(name => ({
                name: name,
                type: getAutoType(name)
            }))
        };
    }
    return { error: new Error('无效数据结构') };
};

// 配置生成器
const autoGenerateConfig = (fields) => {
	const requiredCount = Math.min(Math.floor(fields.length * 0.5), 2);
	let requiredIndex = 0;
	return fields.map((field, index) => {
		const config = {
			key: `k${index}`,
			type: field.type || getAutoType(field.name),
			name: field.name,
			placeholder: getPlaceholder(field),
			required: requiredIndex < requiredCount,
			help: getHelpText(field),
			sfzoptions: {name: '',sex: '',mz: '',day: '',ade: '',old: ''},
			zboptions: [],
			options: '',
			max: 9 // 默认多图上传最大数量
		};
		// 特殊类型处理
		switch (config.type) {
		    case 'image':
			case 'multiimage':
				config.options = '800/600'; // 默认压缩尺寸
				break;
			case 'select':
			case 'muselect':
			case 'radio':
			case 'checkbox':
				config.options = getDefaultOptions(field.name);
				break;
		}
		if (config.Required) requiredIndex++;
		return config;
	});
};

//生成验证规则
const generateRules = (field, isread = false) => {
	const rules = [];
	if (field.required || isread) {
		rules.push({
			required: true,
			message: `${field.name}不能为空`,
			trigger: ['checkbox', 'radio'].includes(field.type) ? 'change' : 'blur'
		})
	}else{
	    return rules; //不是必填不校验
	}
	switch(field.type) {
		case 'mobile':
			rules.push({
				pattern: /^1[3-9]\d{9}$/,
				message: '手机号格式不正确',
				trigger: 'blur'
			})
			break
		case 'idcard':
			rules.push({
				pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/,
				message: '身份证号格式不正确',
				trigger: 'blur'
			})
			break
		case 'number':
			rules.push({
				validator: (_, value, callback) => {
					if (isNaN(value)) {
						callback(new Error('必须为数字'));
					} else {
						callback();
					}
				},
				trigger: 'blur'
			})
			break
		case 'image':
			rules.push({
				validator: (_, value, callback) => {
					const isValid = !!value; // 单图为字符串
					isValid ? callback() : callback('请上传有效图片');
				}
			})
			break
		case 'multiimage':
			rules.push({
				validator: (_, value, callback) => {
					const isValid = Array.isArray(value) && value.length > 0;
					isValid ? callback() : callback('请至少上传一张图片');
				}
			})
			break
		case 'video':
			rules.push({
				validator: (_, value, callback) => {
					const isValid = Array.isArray(value) && value.length > 0;
					isValid ? callback() : callback('请至少上传一个视频');
				}
			})
			break
	}
	return rules
}

// 导出到Excel
const exportToExcel = (filename, data, fieldsMap) => {
    try {
        // 处理表头和数据映射
        const headers = Object.values(fieldsMap);
        const keys = Object.keys(fieldsMap);
        // 构造工作表数据
        const wsData = [
            headers,
            ...data.map(item => keys.map(key => item[key]))
        ]
        // 创建工作表
        const worksheet = XLSX.utils.aoa_to_sheet(wsData);
        // 创建工作簿
        const workbook = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
        // 生成文件并保存
        XLSX.writeFile(workbook, `${filename}_${new Date().toISOString().slice(0, 10)}.xlsx`);
    } catch (error) {
        console.error('导出失败:', error);
        ElMessage.error('文件导出失败');
    }
};

const datelx = {
    date: {
        format: "YYYY-MM-DD",
        valueFormat: "YYYY-MM-DD"
    },
    datetime: {
        format: "YYYY-MM-DD HH:mm:ss",
        valueFormat: "YYYY-MM-DD HH:mm:ss"
    },
    time: {
        format: "HH:mm:ss",
        valueFormat: "HH:mm:ss"
    },
};
// 公共方法获取表单组件属性
const createGetComponentProps = (options) => (field, formData) => {
    let disabled = options.getDisabled(field);
    if(['randomkey','rowcalc', 'stringconcat', 'map'].includes(field.type)) disabled = true;
    field.disabled = disabled;
    // 处理图片上传类型
    if (['image', 'multiimage', 'sczs'].includes(field.type)) {
        const limit = field.type === 'multiimage' && field.max > 0 ? field.max : 9;
        const hasReachedLimit = formData[field.prop]?.length >= limit;
        const isSingleType = ['image', 'sczs'].includes(field.type);
        return {
            action: `${window.APP_CONFIG.baseurl}v1/upload`,
            listType: 'picture-card',
            autoUpload: true,
            multiple: field.type === 'multiimage',
            limit,
            accept: '.jpg,.jpeg,.png',
            class: hasReachedLimit || (isSingleType && formData[field.prop]) ? ['hide-upload'] : [],
            disabled,
            data: {
                imgwh: options.getUploadImgwh(field),
                path: options.getUploadPath(field)
            }
        }
    }
    // 处理普通组件
    const formprops = { ...field.props, disabled };
    switch (field.type) {
        case 'muselect':
            formprops.multiple = true;
            break;
        case 'textarea':
            formprops.type = 'textarea';
            formprops.rows = 4;
            break;
        case 'datetime':
            formprops.type = 'datetime';
            formprops.format = datelx.datetime.format;
            formprops.valueFormat = datelx.datetime.valueFormat;
            break;
        case 'date':
            formprops.type = 'date';
            formprops.valueFormat = datelx.date.valueFormat;
            formprops.format = datelx.date.format;
            break;
        case 'time':
            formprops.type = 'time';
            formprops.valueFormat = datelx.time.valueFormat;
            formprops.format = datelx.time.format;
            break;
        case 'mobile':
            formprops.maxlength = 11;
            formprops.showWordLimit = true;
            break;
        case 'idcard':
            formprops.maxlength = 18;
            formprops.showWordLimit = true;
            break;
        case 'number':
            formprops.type = 'number';
            break;
        case 'richtext':
            formprops.type = 'richtext';
            formprops.path = options.getRichtextPath(field);
            break;
        case 'subtable':
            formprops.columns = field.columns || [];
            break;
    }
    return formprops;
}

//生成随机密钥
const generateRandomKey = (field) => {
	let option = field.options;
	let max = Number(field.max);
	let characters = '';
	let key = '';
	if(option == 'dxxzmsz') {
		characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
	}else if(option == 'dxxzm') {
		characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
	}else if(option == 'dxzm') {
		characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
	}else if(option == 'xxzm') {
		characters = 'abcdefghijklmnopqrstuvwxyz';
	}else if(option == 'sz') {
		characters = '0123456789';
	}else{
		characters = '0123456789';
		max = 3;
	}
	for (let i = 0; i < max; i++) {
		const randomIndex = Math.floor(Math.random() * characters.length);
		key += characters.charAt(randomIndex);
	}
	if(option == 'date') {
		const now = new Date();  
		let sj =  `${now.getFullYear()}${('0' + (now.getMonth() + 1)).slice(-2)}${('0' + now.getDate()).slice(-2)}${('0' + now.getHours()).slice(-2)}${('0' + now.getMinutes()).slice(-2)}${('0' + now.getSeconds()).slice(-2)}`; 
		key = sj + key; // 当日时间+3位数字
	}
	return key;
}

//行内计算、字符拼接
const calculateFields = (values, fields) => {
    const handleStringConcat = (key, field, values) => {
        const formulaParts = field.options.split("++");
        const replaced = formulaParts.map(part => {
            const value = values[part] ?? '';
            return value != null ? String(value) : '';
        });
        values[key] = replaced.join("");
    };
    // 辅助函数：转义正则表达式特殊字符
    const escapeRegExp = (string) => {
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    };
    const handleRowCalc = (key, field, values, fields) => {
        let formula = field.options;
        // 获取所有 fields 的键作为变量名，并按长度降序排序
        const variables = Object.keys(fields).sort((a, b) => {
            // 优先按长度倒序，长度相同按字母倒序
            const lengthDiff = b.length - a.length;
            return lengthDiff !== 0 ? lengthDiff : b.localeCompare(a);
        });
        // 替换所有变量为数值
        variables.forEach(variable => {
            const regex = new RegExp(`\\b${escapeRegExp(variable)}\\b`, 'g');
            const numValue = parseFloat(values[variable]) || 0; // 无效值视为0
            formula = formula.replace(regex, numValue);
        });
        // 计算公式
        try {
            const result = math.evaluate(formula);
            values[key] = result % 1 === 0 ? result.toFixed(0) : result.toFixed(2);
        } catch (error) {
            console.error("计算错误:", error);
            values[key] = '0'; // 错误时设为默认值
        }
    };
    for (const key in fields) {
        const field = fields[key];
        if (field.type === 'stringconcat') {
            handleStringConcat(key, field, values);
        } else if (field.type === 'rowcalc') {
            handleRowCalc(key, field, values, fields); // 传入 fields 用于获取所有变量
        }
    }
};

// 地图定位
const getLocation = async (key, formData) => {
    // 调用腾讯地图逆地理编码 API 将经纬度转换为地址
    const getAddressFromCoordinates = async (latitude, longitude) => {
        const res = await maptoaddress({latitude, longitude});
        return res || '';
    }
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(async (position) => {
            let longitude = position.coords.longitude; // 获取经度
            let latitude = position.coords.latitude; // 获取纬度
            if(!longitude || !latitude) return ElMessage.error("定位失败,位置信息是不可用");
            // 将 WGS84 坐标转换为 GCJ-02 坐标
            let gcj02 = coordtransform.wgs84togcj02(longitude, latitude);
            // 现在 gcj02[0] 是转换后的经度，gcj02[1] 是转换后的纬度
            let address = await getAddressFromCoordinates(gcj02[1], gcj02[0]);
            if(!formData[key]) formData[key] = '';
            formData[key] = `${gcj02[1]},${gcj02[0]},${address}`;
        }, (error) => {
            console.log(error);
            switch (error.code) {
                case error.PERMISSION_DENIED:
                    ElMessage.error("定位失败,用户拒绝请求地理定位");
                    break;
                case error.POSITION_UNAVAILABLE:
                    ElMessage.error("定位失败,位置信息是不可用");
                    break;
                case error.TIMEOUT:
                    ElMessage.error("定位失败,请求获取用户位置超时");
                    break;
                case error.UNKNOWN_ERROR:
                    ElMessage.error("定位失败,定位系统失效");
                    break;
            }
        }, {timeout: 9000});
    } else {
        ElMessage.error("浏览器不支持定位功能");
    }
}


const parsedValue = (value, type) => {
    if (typeof value === 'string' && window.validators.isJSON(value)) {
        try {
            const parsed = JSON.parse(value);
            if (type === 'area') {
                return parsed.join('');
            }
            return parsed.toString();
        } catch (e) {
            return value || '';
        }
    }
    return '';
};

// 默认值
const getDefaultValue = (field) => {
    switch (field.type) {
        case 'checkbox':
        case 'muselect':
        case 'video':
        case 'multiimage':
        case 'multifile':
        	return []
        case 'number':
        	return field.default || 0
        case 'sczs':
        case 'file':
        case 'image':
        	return ''
        case 'randomkey':
            return generateRandomKey(field)
        default:
        	return field.default || ''
    }
}

// 导出数据
// const exportToExcel = (filename, tableData, zdsz) => {
//     const workbook = new ExcelJS.Workbook();
//     const worksheet = workbook.addWorksheet('Sheet1');
//     const headers = Object.values(zdsz);
//     worksheet.addRow(headers); // 添加表头
//     tableData.forEach((row, rowIndex) => {
//         const rowData = headers.map(header => {
//             const key = Object.keys(zdsz).find(key => zdsz[key] === header);
//             // 如果是要插入图片的列，就直接返回空字符串
//             if (key === 'qm' && row[key] && window.validators.isBase64Image(row[key])) {
//                 return ''; // 清空文本
//             }
//             return key ? row[key] || '' : '';
//         });
//         // 插入图片并设置行高
//         headers.forEach((header, colIndex) => {
//             const key = Object.keys(zdsz).find(key => zdsz[key] === header);
//             if (key === 'qm' && row[key]) { // 检查是否为 Base64 图片
//                 const base64Image = row[key];
//                 if (window.validators.isBase64Image(base64Image)) {
//                     const imageId = workbook.addImage({
//                         base64: base64Image,
//                         extension: 'png' // 假设是 PNG 格式
//                     });
//                     // 在当前行的对应列插入图片
//                     worksheet.addImage(imageId, {
//                         tl: { col: colIndex, row: rowIndex + 1 }, // 加 1 是因为表头占用第一行
//                         ext: { width: 80, height: 30 } // 设置图片大小
//                     });
//                 }
//             }
//         });
//         worksheet.getRow(rowIndex + 1).height = 30; // 行高固定为 40
//         worksheet.addRow(rowData); // 添加行数据，行数据中已经处理了清空文本的情况
//     });
//     // 导出文件
//     workbook.xlsx.writeBuffer().then((buffer) => {
//         const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
//         const link = document.createElement('a');
//         link.href = URL.createObjectURL(blob);
//         link.download = `${filename}.xlsx`;
//         link.click();
//     });
// };